/*
 * NetworkServer.cpp
 *
 *  Created on: Apr 12, 2011
 *      Author: Alan Mattila
 */

#include <iostream>
#include <cassert>

#include <QTcpServer>
#include <QTcpSocket>
#include <QByteArray>

#include "NetworkServer.h"
#include "../misc/debug.h"

using std::cout;
using std::endl;


NetworkServer::NetworkServer()
{
	TRACE("Start: NetworkServer constructor\n");
	packet = NULL;
	checkers = NULL;
	tcpSocket = NULL;

	// Create the QTcpServer
	tcpServer = new QTcpServer();
	tcpServer->setProxy(QNetworkProxy::NoProxy);
	TRACE("End: NetworkServer constructor\n");
}


NetworkServer::NetworkServer(Checkers *_checkers)
{
	TRACE("Start: NetworkServer constructor\n");
	packet = NULL;
	checkers = _checkers;
	tcpSocket = NULL;

	// Create the QTcpServer
	tcpServer = new QTcpServer();
	tcpServer->setProxy(QNetworkProxy::NoProxy);
	TRACE("End: NetworkServer constructor\n");
}


NetworkServer::~NetworkServer()
{
	TRACE("Start: NetworkServer destructor\n");
	// Delete the QTcpSocket
	if(tcpSocket)
		delete tcpSocket;

	// Delete the QTcpServer
	if(tcpServer)
		delete tcpServer;

	// Delete the packet
	if(packet)
		delete packet;
	TRACE("End: NetworkServer destructor\n");
}


/*********************************************************
 * int NetworkServer::StartHosting()
 * Causes the server to start hosting.
 * More specifically, it listens for a client connection.
 ********************************************************/
int NetworkServer::StartHosting()
{
	TRACE("tcpServer = 0x%x\n", tcpServer);
	// Listen for incoming connections
	if(!tcpServer->listen(QHostAddress::Any, (quint16) port))
	{
		TRACE("Error: at QTcpServer::listen()\n");
		TRACE("    port = %d\n", port);
		return NETWORK_ERROR;
	}
	TRACE("\n");

	// Wait for a client to connect
	if(!tcpServer->waitForNewConnection(-1, NULL))
	{
		TRACE("Error: at QTcpServer::waitForNewConnection()\n");
		return NETWORK_ERROR;
	}

	// Accept the incoming connection
	tcpSocket = tcpServer->nextPendingConnection();
	assert(tcpSocket != NULL);
	connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(Error()));
	connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(Disconnected()));
	connect(tcpSocket, SIGNAL(bytesWritten(qint64)), this, SLOT(BytesWritten()));
	connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(GetPacketFromClient()));

	// Create the block of data to send
	QByteArray block;
	int packetLength = CreateGameDataPacket(block);

	// Write the data to the socket
	TRACE("Writing block of data to tcpSocket\n");
	qint64 numBytes = tcpSocket->write(block);
	TRACE("%d bytes written\n", (int) numBytes);

	return 0;
}


/*********************************************************
 * int NetworkServer::MakeMove()
 * This sends updated information to the client.
 * This is intended to be called once the human player
 * has made a valid move.
 ********************************************************/
int NetworkServer::MakeMove()
{
	TRACE("Sending Game Data packet\n");

	// Create the block of data to send
	QByteArray block;
	int packetLength = CreateGameDataPacket(block);

	// Write the data to the socket
	TRACE("Writing block of data to tcpSocket\n");
	qint64 numBytes = tcpSocket->write(block);
	TRACE("%d bytes written\n", (int) numBytes);

	return 0;
}


void NetworkServer::SendIllegalMove(void)
{
	TRACE("Sending IllegalMove packet\n");

	// Create the block of data to send
	QByteArray block;
	CreateMessagePacket(IllegalMove, block);

	// Write the data to the socket
	TRACE("Writing block of data to tcpSocket\n");
	qint64 numBytes = tcpSocket->write(block);
	TRACE("%d bytes written\n", (int) numBytes);
}


/*********************************************************
 * void NetworkServer::CreateMessagePacket()
 ********************************************************/
void NetworkServer::CreateMessagePacket(packetMessage_t packetMessage, QByteArray &block)
{
	// Create a new packet
	assert(packet == NULL);
	packet = new Packet();

	// Create a new data stream
	QDataStream out(&block, QIODevice::WriteOnly);
	out.setVersion( QDataStream::Qt_4_7 );

	// Assemble the packet
	packet->SetPacketType(Message);
	packet->SetPacketMessage(packetMessage);

	// Copy packet data into QByteArray
	int packetSize = packet->GetPacketSize();
	out << (uint8_t)packetSize;
	for(int i=0; i<packetSize; i++)
	{
		out << packet->GetByte(i);
	}

	// TEST: display the contents of the packet
	packet->DisplayContents();

	// Delete the packet
	if(packet)
		delete packet;
	packet = NULL;
}


/*********************************************************
 * void NetworkServer::CreateGameDataPacket()
 * Returns the length (number of bytes) of the packet.
 ********************************************************/
int NetworkServer::CreateGameDataPacket(QByteArray &block)
{
	// Create a new packet
	//assert(packet == NULL);
	packet = new Packet();

	// Create a new data stream
	QDataStream out(&block, QIODevice::WriteOnly);
	out.setVersion( QDataStream::Qt_4_7 );

	// Assemble the packet
	packet->SetPacketType(GameData);
	packet->SetPacketStatus(checkers->GetStatus());
	packet->SetPacketGameBoard(checkers->GetBoard());

	// Copy packet data into QByteArray
	int packetSize = packet->GetPacketSize();
	out << (uint8_t)packetSize;
	for(int i=0; i<packetSize; i++)
	{
		out << packet->GetByte(i);
	}

	// TEST: display the contents of the packet
	packet->DisplayContents();

	// Delete the packet
	if(packet)
		delete packet;
	packet = NULL;

	return packetSize;
}


/*********************************************************
 * void NetworkServer::GetPacketFromServer()
 ********************************************************/
int NetworkServer::GetPacketFromClient(void)
{
	blockSize = 0;
	TRACE("Start: GetPacketFromClient()\n");
	assert(tcpSocket != NULL);
	TRACE("tcpSocket->bytesAvailable() = %d\n", tcpSocket->bytesAvailable());

	QDataStream in(tcpSocket);
	in.setVersion(QDataStream::Qt_4_7);

	//while(tcpSocket->bytesAvailable() < (int)sizeof(quint8)) { }
	in >> blockSize;
	TRACE("Read blockSize = %d\n", blockSize);

	while(tcpSocket->bytesAvailable() < blockSize) { }
	TRACE("Reading in all data\n");
	QByteArray data = tcpSocket->readAll();
	TRACE("data.size() = %d\n", data.size());

	// Create new packet
	assert(packet == NULL);
	packet = new Packet();

	// Copy data into Packet class
	for(int i=0; i<(int)blockSize; i++)
	{
		//TRACE("data[%2d]: %d\n", i, data[i]);
		packet->SetByte(data[i], i);
	}

	blockSize = 0;

	// Now parse the packet
	return ParsePacket();
}


/*********************************************************
 * void NetworkServer::ParsePacket()
 ********************************************************/
int NetworkServer::ParsePacket(void)
{
	TRACE("Start: ParsePacket()\n");
	assert(packet != NULL);
	packet->DisplayContents();

	// Check the packet type
	if(packet->GetPacketType() == Move)
	{
		TRACE("Packet Type: Move\n");
		ParseMove(packet->GetPacketX1(), packet->GetPacketY1(), packet->GetPacketX2(), packet->GetPacketY2());
	}
	else
	{
		TRACE("Error: unknown packet type!\n");
	}

	// Delete the packet
	if(packet)
		delete packet;
	packet = NULL;

	TRACE("End: ParsePacket()\n");

	return 0;
}


void NetworkServer::ParseMove(int x1, int y1, int x2, int y2)
{
	int result = checkers->Move(x1, y1, x2, y2);
	if(result == MOVE_FAIL)
	{
		// Send IllegalMove
		SendIllegalMove();
	}
	else
	{
		// Send updated game board
		MakeMove();
	}
}


void NetworkServer::Error(void)
{
	TRACE("");
	qDebug() << "Received error() signal!";
	qDebug() << "  " << tcpSocket->errorString();
}


void NetworkServer::BytesWritten(void)
{
	TRACE("");
	qDebug() << "Received bytesWritten() signal!";
}


void NetworkServer::Disconnected(void)
{
	TRACE("");
	qDebug() << "Received disconnected() signal!";
}
